LambdaでEJSを使ってみる
今回はNode.jsでejsというテンプレートエンジンのパッケージを使ってEJSテンプレートからHTMLファイルを生成する方法をご紹介します。
環境
- Lambda Node.js10
- aws-cli/1.16.158
ejsの用途
ただ単に変数を置換するだけであれば、Javascriptの replace
で可能ですが、テンプレート内で条件分岐やループを行いたい場合、ejsを使えば簡単にできます。
試してみる
ejsをLambdaから呼び出すためにLayer化します。(ZIPアップロードでも大丈夫です)
S3の構成
今回はお試しということでデプロイ時のソース置き場とテンプレート置き場とHTMlファイルの出力先を一つのバケットを使います。 それぞれ以下のキーを使用します。
- デプロイ時のソース置き場
- source
- テンプレート置き場
- template
- HTMlファイルの出力先
- output
プロジェクト構成
最終的な構成は以下となります。
. ├── cfn │ └── template.yml ├── functions │ └── generate_html │ └── index.js ├── layer │ └── nodejs │ ├── node_modules │ │ └── ejs │ ├── package-lock.json │ └── package.json ├── output │ ├── ejs-layer.zip │ └── packaged.yml └── template └── template.ejs
HTMLテンプレートの作成
簡易的ですが、以下のテンプレートをS3に置いておきます。
template.ejs
<h2>Classmethodの領収書</h2> <% if (user) { %> <h3><%=user.username%>様</h3> <% } %> ¥<%=amount%>
S3へアップロード
$ aws s3 cp template/template.ejs s3://{バケット名}/template/
Layerの作成
zipファイルの作成
ejsのパッケージを含んだzipファイルを作成します。
$ mkdir -p layer/nodejs $ cd layer/nodejs $ npm init -y $ npm install --save ejs $ cd ../ $ zip -r ../output/ejs-layer.zip .
layer/nodejsの階層でディレクトリを作成して、/nodejs配下にejsをインストールします。 インストールできたらnodejs/配下をzip化します。
S3の source/layer/
配下にアップロードします。
$ aws s3 cp /output/ejs-layer.zip s3://{バケット名}/source/layer/
デプロイはLambdaと同じSAMテンプレートで行うので先にLambdaを実装します。
Lambdaの作成
Lambdaのコード
generate_html/index.js
const AWS = require('aws-sdk'); const s3 = new AWS.S3(); const ejs = require('ejs'); const bucket = process.env['BUCKET_NAME'] const getTemplate = async (template_name) => { const params = { Bucket: bucket, Key: `template/${template_name}`, }; const data = await s3.getObject(params).promise(); return new String(data.Body, 'utf8'); }; const putHtml = async (html) => { const file_name = Math.floor(new Date().getTime()/1000); const file_path = `output/${file_name}` const params = { Bucket: bucket, Key: file_path, Body: html, ContentType: 'text/html' }; await s3.putObject(params).promise(); return file_path; }; exports.handler = async (event) => { console.log(event); const template = await getTemplate('template.ejs'); let params = { amount: event.amount }; params.user = event.user ? event.user : null; const html = ejs.render(template, params); const file_path = await putHtml(html); const response = { statusCode: 200, body: JSON.stringify(file_path), }; return response; };
- まず
getTemplate
で先程準備したEJSテンプレートをS3からダウンロードします。 - eventからパラメータの値を受け取り、
ejs.render()
で置換します。 - パラメータの置換した後に
putHtml
でS3のoutput/配下に出力したHTMLをアップロードします。
S3にアップロードするときの注意点ですが、 ContentType: 'text/html'
と明示しておかないと、ブラウザから実際に出力したHTMLのページを開く時に閲覧でなくダウンロードになってしまいます。
SAMテンプレート
template.yml
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Parameters: BucketName: Description: Load template and output destination of generated html file Type: String Default: {バケット名} Resources: GenerateHtml: Type: AWS::Serverless::Function Properties: FunctionName: generate_html CodeUri: './functions/generate_html/index.js' Handler: index.handler MemorySize: 128 Runtime: nodejs10.x Timeout: 300 Policies: - AWSLambdaBasicExecutionRole - AmazonS3FullAccess Environment: Variables: BUCKET_NAME: !Ref BucketName Layers: - !Ref EjsLayer GenerateHtmlFuncLogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub /aws/lambda/${GenerateHtml} RetentionInDays: 7 EjsLayer: Type: AWS::Serverless::LayerVersion Properties: LayerName: ejsLayer ContentUri: Bucket: !Ref BucketName Key: 'source/layer/ejs-layer.zip' CompatibleRuntimes: - nodejs10.x RetentionPolicy: Retain
Lambdaパッケージ化
$ sam package \ --template-file cfn/template.yml \ --s3-bucket {バケット名} \ --s3-prefix source \ --output-template-file ./output/packaged.yml
Lambdaデプロイ
$ sam deploy \ --stack-name generate-html \ --template-file ./output/packaged.yml \ --capabilities CAPABILITY_NAMED_IAM
テスト
Lambdaコンソールから実際に実行してみます。
テスト
を選択してテストイベントを作成します。
イベントのパラメータに以下を設定します。
ステータスが200で返ってきました。bodyにはS3出力先のキーが入っています。
S3コンソールからレスポンスに含まれたキーを開きます。
S3からダウンロードして開いてみると想定通りにHTMLが生成できています。